package com.gmrz.uap.authmetadata;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.gmrz.uap.cache.AmmsCacheUtil;
import com.gmrz.uap.cfg.SystemConst;
import com.gmrz.uap.common.AuthTypeInterceptor;
import com.gmrz.uap.common.ControllerSupport;
import com.gmrz.uap.common.DbConst;
import com.gmrz.uap.dict.DictConst;
import com.gmrz.uap.dict.DictService;
import com.gmrz.uap.model.AuthMetadata;
import com.gmrz.uap.model.Dict;
import com.gmrz.uap.model.Policy;
import com.gmrz.uap.operlog.OperLogService;
import com.gmrz.uap.resource.ResourceInterceptor;
import com.gmrz.uap.util.Identities;
import com.gmrz.uap.util.JSONUtils;
import com.jfinal.aop.Before;
import com.jfinal.kit.PropKit;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Page;
import com.jfinal.upload.ExceededSizeException;
import com.jfinal.upload.UploadFile;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import redis.clients.jedis.exceptions.JedisConnectionException;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class AuthMetadataController extends ControllerSupport {

    private static final Logger LOG = LogManager.getLogger(AuthMetadataController.class);

    /**
     * 元数据主页面
     */
    @Before({ResourceInterceptor.class, AuthTypeInterceptor.class})
    public void index() {
        List<Dict> authTypeList = DictService.me.getDictListByDictTypeID(DictConst.DICT_TYPE_STATICS_AUTH_AUTH_TYPE_ID);
        setAttr("authTypeList", authTypeList);
    }

    /**
     * 根据前台发过来的参数进行分页,查询
     */
    public void list() {

        int page = getParaToInt(SystemConst.DATATABLE_PAGINATE_OFFSET, SystemConst.DATATABLE_PAGINATE_OFFSET_DEFAULT);
        int rows = getParaToInt(SystemConst.DATATABLE_PAGINATE_LIMIT, SystemConst.DATATABLE_PAGINATE_LIMIT_DEFAULT);

        String sort = getPara(SystemConst.DATATABLE_PAGINATE_SORT, SystemConst.DEFAULT_ORDERBY_CREATETS);
        String order = getPara(SystemConst.DATATABLE_PAGINATE_ORDER, SystemConst.DATATABLE_PAGINATE_ORDER_DEFAULT);

        try {
            AuthMetadata authMetadata = getModel(AuthMetadata.class);

            Page<AuthMetadata> pages = AuthMetadataService.me.findList4Paginate(authMetadata, page, rows, sort, order);
            result.put(SystemConst.DATATABLE_PAGINATE_TOTAL, pages.getTotalRow());
            result.put(SystemConst.DATATABLE_PAGINATE_ROWS, pages.getList());
        } catch (Exception e) {
            LOG.error("搜索关键字格式错误", e);
            result.put(SystemConst.DATATABLE_PAGINATE_TOTAL, 0);
            result.put(SystemConst.DATATABLE_PAGINATE_ROWS, new ArrayList<AuthMetadata>());
        }

        render(responseJsonRender(result));
    }

    /**
     * 增加页面跳转
     */
    public void add() {
        setAttr("authTypeList", DictService.me.getDictListByDictTypeID(DictConst.DICT_TYPE_STATICS_AUTH_AUTH_TYPE_ID));
        render("form" + SystemConst.VIEW_EXTENDSION);
    }

    /**
     * 根据ID删除数据信息，可以同时删除多个，数据使用逗号(,)隔开
     */
    public void remove() {
        String ids = getPara("idList");
        String aaid = getPara("aaidList");


        if (StrKit.isBlank(ids)) {
            message = getRes().get("system.validator.paramnull");
            result.put("flag", flag);
            result.put("message", message);
            render(responseJsonRender(result));
            return;
        }


        //检查AAID是否已经被使用
        StringBuilder aaidSb = new StringBuilder();
        for (String id : ids.split(SystemConst._DEFAULT_SEPARATOR_COMMA)) {
            if (AuthMetadataService.me.checkAaidIsUsing(id) || AuthMetadataService.me.checkAaidIsRegisterUsing(id)) {
                AuthMetadata authMetadataTmp = AuthMetadata.dao.findById(id);
                aaidSb.append(authMetadataTmp.getAaid()).append(",");
            }
        }

        if (!StrKit.isBlank(aaidSb.toString())) {
            message = aaidSb.toString();
            message += getRes().get("auth.form.remove.failure.used");
            result.put("flag", flag);
            result.put("message", message);
            render(responseJsonRender(result));
            return;
        }

        try {
            flag = AuthMetadataService.me.remove(ids);

            if (flag) {
                message = getRes().get("auth.form.remove.success");

                message = AmmsCacheUtil.deleteIfSuccess(AmmsCacheUtil.POLICYJSON, message);//删除缓存中的策略

                String[] idArr = ids.split(SystemConst._DEFAULT_SEPARATOR_COMMA);
                String[] aaidArr = aaid.split(SystemConst._DEFAULT_SEPARATOR_COMMA);

                message = removeCache(idArr, aaidArr, message);//删除缓存中的信息

            } else {
                message = getRes().get("auth.form.remove.failure");
            }
        } catch (JedisConnectionException jedisConnectionException) {
            message += getRes().get("redis.connection.failed");
            LOG.error(message, jedisConnectionException);
        }

        //增加系统操作日志
        OperLogService.me.addOperLog(getRes().get("auth.form.remove"), aaid.replace("_", "#"), flag, message, getRequest());

        LOG.info("删除元数据：" + aaid.replace("_", "#"));
        result.put("flag", flag);
        result.put("message", message);
        render(responseJsonRender(result));
    }


    public void save() {
        int uploadFileSize = PropKit.use("application.properties").getInt("upload.file.size");
        String filePath = Identities.uuid2();
        AuthMetadata authMetadata;

        try {
            UploadFile uploadFile = getFile("uploadFiles", filePath, uploadFileSize * 1024 * 1024);
            String filename = uploadFile.getFileName();
            filePath = uploadFile.getUploadPath();
            authMetadata = getModel(AuthMetadata.class);

            if (!filename.toLowerCase().endsWith(".json")) {//如果文件格式不是json

                FileUtils.deleteDirectory(new File(filePath));

                result.put(SystemConst.RESPONSE_RESULT_FLAG, flag);
                result.put(SystemConst.RESPONSE_RESULT_MESSAGE, "元数据格式不正确，请重新选择");
                render(responseJsonRender(result));
                return;
            }

            File file = uploadFile.getFile();
            JSONObject authMetadataJson = JSONObject.parseObject(JSONUtils.getJSON(file));

            String aaid = authMetadataJson.getString(DbConst.TABLE_AUTH_METADATA_COLUMN_AAID);
            if (aaid == null && authMetadataJson.containsKey(SystemConst.PROTOCOL_FIDO2_AAGUID)){
                aaid = authMetadataJson.getString(SystemConst.PROTOCOL_FIDO2_AAGUID);
            }

            //根据AAID获取元数据信息
            AuthMetadata authMetadataTemp = AuthMetadataService.me.findByAAID(aaid);

            if (null != authMetadataTemp) {//如果元数据已经存在则更新现有元数据信息

                authMetadataTemp.setAuthVersion(authMetadataJson.getInteger(SystemConst.AUTHMETADATA_AUTHENTICATORVERSION));
                authMetadataTemp.setDescription(authMetadataJson.getString(DbConst.TABLE_AUTH_METADATA_COLUMN_DESCRIPTION));
                authMetadataTemp.setJsonData(authMetadataJson.toString());
                authMetadataTemp.set(DbConst.TABLE_AUTH_METADATA_COLUMN_CREATETS, DbConst.getDatabaseDateTime());
                if (!authMetadataTemp.getAuthType().equals(authMetadata.getAuthType())) {
                    if (AuthMetadataService.me.checkAaidIsUsing(authMetadataTemp.getMetadataId())) {
                        result.put(SystemConst.RESPONSE_RESULT_FLAG, flag);
                        result.put(SystemConst.RESPONSE_RESULT_MESSAGE, "元数据存在策略中");
                        render(responseJsonRender(result));
                        return;
                    }
                }

                authMetadataTemp.setAuthType(authMetadata.getAuthType());


                flag = authMetadataTemp.update();
            } else {
                if (aaid.length() == SystemConst.AAID_LENGTH) {//如果AAID的长度为9则设置协议为UAF
                    authMetadata.setProtocol(SystemConst.PROTOCOL_UAF);
                } else {
                    authMetadata.setProtocol(SystemConst.PROTOCOL_FIDO2);
                }
                authMetadata.setAaid(aaid);
                authMetadata.setAuthVersion(authMetadataJson.getInteger(SystemConst.AUTHMETADATA_AUTHENTICATORVERSION));
                authMetadata.setDescription(authMetadataJson.getString(DbConst.TABLE_AUTH_METADATA_COLUMN_DESCRIPTION));
                authMetadata.setJsonData(authMetadataJson.toString());
                authMetadata.put(DbConst.TABLE_AUTH_METADATA_COLUMN_METADATA_ID, Identities.uuid2());
                authMetadataTemp = authMetadata;

                flag = AuthMetadataService.me.saveAuthMetadata(authMetadata, getCookie(SystemConst.COOKIE_TENANT_ID));
            }

            //removeCache(authMetadataTemp.getMetadataId(), authMetadataTemp.getAaid(), true, message);//移除缓存中的元数据信息

            if (flag) {
                if(authMetadataJson.containsKey(SystemConst.AUTH_METADATA_NODE_SECURELEVEL)){
                    // 元数据中安全等级节点
                    int secureLevel = authMetadataJson.getInteger(SystemConst.AUTH_METADATA_NODE_SECURELEVEL);
                    String levelType = null ;
                    if (SystemConst.AUTH_METADATA_NODE_SECURELEVEL_1 == secureLevel){
                        levelType = SystemConst.CERT_SECURE_LEVEL_SE;
                    }else if(SystemConst.AUTH_METADATA_NODE_SECURELEVEL_2 == secureLevel){
                        levelType = SystemConst.CERT_SECURE_LEVEL_TEEANDTUI;
                    }else {
                        levelType = SystemConst.CERT_SECURE_LEVEL_TEE;
                    }
                    List<Dict> dicts = Dict.dao.findDictByNameText(aaid);
                    if (dicts != null && dicts.size()>0){
                        for (Dict dict : dicts){
                            Dict.dao.deleteById(dict.getDictId());
                        }
                    }
                    Dict.dao.dictSave(aaid, levelType);
                }
                message = aaid + getRes().get("auth.form.add.success");
            } else {
                message = aaid + getRes().get("auth.form.add.failure");
            }
            message = removeCache(authMetadataTemp.getMetadataId(), authMetadataTemp.getAaid(), true, message);//移除缓存中的元数据信息

            FileUtils.deleteDirectory(new File(filePath));

        } catch (ExceededSizeException exceededSizeException) {
            message = "上传文件大小超过系统限制" + uploadFileSize + "M";
            LOG.error(message, exceededSizeException);
        } catch (IOException ioException) {
            LOG.error("删除目录出现异常", ioException);
        } catch (JSONException jsonException) {
            message = "元数据内容解析现异常，请检查文件是否正确";
            LOG.error(message, jsonException);
        } catch (RuntimeException runtimeException) {
            message = "元数据解析现异常，请检查文件是否正确";
            LOG.error(message, runtimeException);
        }

        //增加系统操作日志
        OperLogService.me.addOperLog("增加元数据", message, flag, message, getRequest());

        result.put(SystemConst.RESPONSE_RESULT_FLAG, flag);
        result.put(SystemConst.RESPONSE_RESULT_MESSAGE, message);

        render(responseJsonRender(result));
    }


    public void checkAAIDExist() {
        int uploadFileSize = PropKit.use("application.properties").getInt("upload.file.size");
        String filePath = Identities.uuid2();

        try {
            UploadFile uploadFile = getFile("uploadFiles", filePath, uploadFileSize * 1024 * 1024);
            String filename = uploadFile.getFileName();
            filePath = uploadFile.getUploadPath();

            if (!filename.toLowerCase().endsWith(".json")) {//如果文件格式不是json

                FileUtils.deleteDirectory(new File(filePath));

                result.put(SystemConst.RESPONSE_RESULT_FLAG, flag);
                result.put(SystemConst.RESPONSE_RESULT_MESSAGE, "元数据格式不正确，请重新选择");
                render(responseJsonRender(result));
                return;
            }

            File file = uploadFile.getFile();
            JSONObject authMetadataJson = JSONObject.parseObject(JSONUtils.getJSON(file));
            String aaid = authMetadataJson.getString(DbConst.TABLE_AUTH_METADATA_COLUMN_AAID);
            if ((null != aaid && !"".equals(aaid.trim())) ||
                    (authMetadataJson.containsKey(SystemConst.PROTOCOL_FIDO2_AAGUID) &&
                            ((aaid = authMetadataJson.getString(SystemConst.PROTOCOL_FIDO2_AAGUID))==
                                    authMetadataJson.getString(SystemConst.PROTOCOL_FIDO2_AAGUID)))) {
                //根据AAID获取元数据信息
                AuthMetadata authMetadataTemp = AuthMetadataService.me.findByAAID(aaid);

                if (null != authMetadataTemp) {
                    flag = true;
                }

                if (flag) {
                    message = authMetadataJson.getString(DbConst.TABLE_AUTH_METADATA_COLUMN_AAID) + "元数据已经存在，如果提交数据将对已保存的数据进行覆盖";
                } else {
                    message = authMetadataJson.getString(DbConst.TABLE_AUTH_METADATA_COLUMN_AAID) + "元数据不存在，可以进行提交";
                }
            } else {
                message = "元数据解析现异常，请检查文件是否正确";
            }
            FileUtils.deleteDirectory(new File(filePath));

        } catch (ExceededSizeException exceededSizeException) {
            message = "上传文件大小超过系统限制" + String.valueOf(uploadFileSize) + "M";
            LOG.error(message, exceededSizeException);
        } catch (IOException ioException) {
            LOG.error("删除目录出现异常", ioException);
        } catch (JSONException jsonException) {
            message = "元数据内容解析现异常，请检查文件是否正确";
            LOG.error(message, jsonException);
        } catch (RuntimeException runtimeException) {
            message = "元数据解析现异常，请检查文件是否正确";
            LOG.error(message, runtimeException);
        } catch (Exception e) {
            message = "元数据解析现异常，请检查文件是否正确";
            LOG.error(message, e);
        }


        result.put(SystemConst.RESPONSE_RESULT_FLAG, flag);
        result.put(SystemConst.RESPONSE_RESULT_MESSAGE, message);

        render(responseJsonRender(result));

    }

    private List<File> getUploadJsonFile(List<UploadFile> uploadJsonFile) {
        List<File> files = new ArrayList<File>();
        for (UploadFile uploadFile : uploadJsonFile) {
            files.add(uploadFile.getFile());
        }
        return files;
    }

    public void getAuthMetadataByPolicyId() {
        Policy policy = getModel(Policy.class);
        List<AuthMetadata> authMetadataList = AuthMetadataService.me.getAuthMetadataByPolicyId(policy.getPlId());
        render(responseJsonRender(authMetadataList));
    }

    public void getNonAuthMetadataByPolicyId() {
        Policy policy = getModel(Policy.class);
        if (StringUtils.isNotBlank(policy.getPlId())) {
            policy = Policy.dao.findById(policy.getPlId());
        } else {
            policy.setPlId("none");
            policy.setAuthType("none");
        }
        List<AuthMetadata> authMetadataList = AuthMetadataService.me.getNonAuthMetadataByPolicyId(policy);
        render(responseJsonRender(authMetadataList));
    }


    public static String  removeCache(String[] idArr, String[] aaidArr,String messages) {
        for (int i = 0; i < idArr.length; i++) {
            messages = removeCache(idArr[i], aaidArr[i], false, messages);
            if (messages.contains("没有连接上redis!") || messages.contains("缓存数据删除失败!")){
                return messages;
            }
        }
        messages = AmmsCacheUtil.deleteIfSuccess(AmmsCacheUtil.UAP_SPEC_LIST, messages);
        return messages;
    }


    public static String removeCache(String id, String aaid, boolean flag, String messages) {
        String tmpCacheKey = AmmsCacheUtil.UAP_SPEC_ID + id;
        messages = AmmsCacheUtil.deleteIfSuccess(tmpCacheKey, messages);//根据元数据ID删除缓存中的元数据信息

        tmpCacheKey = AmmsCacheUtil.UAP_SPEC_AAID + aaid;
        messages = AmmsCacheUtil.deleteIfSuccess(tmpCacheKey, messages);//根据元数据名称删除缓存中的元数据信息

        if (flag) {
            messages = AmmsCacheUtil.deleteIfSuccess(AmmsCacheUtil.UAP_SPEC_LIST, messages);
        }
        return messages;
    }

}
